#pragma once
#include <future>
#include <thread>
#include <vector>
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <stdexcept>
#include "../../config/client/ClientThreadExecutionConfig.hpp"
#include "../interface/ClientExecutionInterface.hpp"
#include "../../type/ClientType.hpp"


class ClientThreadExecution:public ClientExecutionInterface{



private:
    static ClientType::EXECUTION_TYPE executionType;



    std::vector<std::thread> m_threads;

    std::queue<std::function<void()>> m_tasks;

    std::mutex m_tasksMutex;

    std::condition_variable m_condition;

    std::queue<ClientExecutionInterface*> m_threadExecutionsQueue;

    std::function<void()> m_threadConstructParameterFun = [this]() {
        while (true) {
            std::function<void()> task;
            {
                std::unique_lock<std::mutex> lock(this->m_tasksMutex);

                this->m_condition.wait(lock,[this] {return !this->m_isRunning || !this->m_threads.empty();});

                if (!this->m_isRunning && this->m_threads.empty()) return;

                task = std::move(this->m_tasks.front());
                this->m_tasks.pop();
            }

            task();
        }
    };


private:

    /**
     *
     * @param quantity
     */
    void initializePool(int quantity) {
        for (int i = 0; i < quantity; i++)
            this->m_threads.emplace_back(this->m_threadConstructParameterFun);
    }



public:
    ClientThreadExecution()  = default;
    ~ClientThreadExecution() override  = default;


    /**
     * 获取执行器类型
     * @return
     */
    ClientType::EXECUTION_TYPE getType() const override {
        return ClientThreadExecution::executionType;
    }




    /**
     *任务提交
     * @tparam F
     * @tparam ARGS
     * @param f
     * @param args
     * @return
     */
    template<typename F,typename ...ARGS>
    std::future<typename std::result_of<F(ARGS...)>::type> enqueue(F&& f,ARGS&& ...args) {

        using returnType = typename std::result_of<F(ARGS...)>::type;
        std::shared_ptr<std::packaged_task<returnType()>> task = std::make_shared<std::packaged_task<returnType()>>(
            std::bind(std::forward<F>(f), std::forward<ARGS>(args)...)
            );

        std::future<returnType> res = task->get_future();

        {
            std::unique_lock<std::mutex> lock(this->m_tasksMutex);
            if (!this->m_isRunning) throw std::runtime_error("任务提交停止！");
            this->m_tasks.emplace([task](){(*task)();});

            /* push传入的对象需要事先构造好，再复制过去插入容器中；
              而emplace则可以自己使用构造函数所需的参数构造出对象，并直接插入容器中。
              emplace相比于push省去了复制的步骤，则使用emplace会更加节省内存。*/
        }
        this->m_condition.notify_one();

        return res;
    }


    /**
     *
     * @param execution
     */
    void addThreadExecution(ClientExecutionInterface* execution) {
        this->m_threadExecutionsQueue.emplace(execution);
        this->enqueue([execution] {
            std::bind(&ClientExecutionInterface::start,execution);
        });
    }



private:



    /**
     *
     * @return
     */
    bool startCallBackFunction() override {

        ClientThreadExecutionConfig* config = (ClientThreadExecutionConfig*) this->m_config;

        //初始化线程池
        this->initializePool(config->threadPoolSize);


        return true;
    }


    /**
     *
     * @return
     */
    bool closeCallBackFunction() override {

        //添加关闭所有多线程执行器的任务
        while (!this->m_threadExecutionsQueue.empty()) {
            ClientExecutionInterface* execution = this->m_threadExecutionsQueue.front();

            this->enqueue([execution]() {
                std::bind(&ClientExecutionInterface::close,execution);
            });

            this->m_threadExecutionsQueue.pop();
        }

        //唤醒所有线程
        this->m_condition.notify_all();

        //确保所有线程正确退出
        for (auto& thread : this->m_threads)
            thread.detach();

        return true;
    }






public:




};






ClientType::EXECUTION_TYPE ClientThreadExecution::executionType = ClientType::EXECUTION_TYPE::CLIENT_THREAD_EXECUTION;